package com.codemeta.bokit.web.core.handler;

import cn.hutool.core.util.StrUtil;
import com.codemeta.bokit.comm.exception.CommException;
import com.codemeta.bokit.comm.global.DefaultErrorCode;
import com.codemeta.bokit.comm.global.ResponseResult;
import com.codemeta.bokit.comm.kit.ExceptionKit;
import java.util.Objects;
import java.util.stream.Collectors;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

/**
 * 全局异常处理
 *
 * @author hice
 */
@Slf4j
@RestController
@ControllerAdvice
public class WebExceptionHandler {

    /**
     * 请求参数缺少异常捕获
     *
     * @param ex exception
     * @return response
     */
    @ResponseBody
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public ResponseResult<?> missRequestParamException(MissingServletRequestParameterException ex) {
        log.error(ExceptionKit.buildErrorMessage(ex));
        return new ResponseResult<>(DefaultErrorCode.param);
    }

    /**
     * 请求参数缺少异常捕获
     *
     * @param ex exception
     * @return response
     */
    @ResponseBody
    @ExceptionHandler(ValidationException.class)
    public ResponseResult<?> missRequestParamException(ValidationException ex) {
        log.error(ExceptionKit.buildErrorMessage(ex));
        return new ResponseResult<>(DefaultErrorCode.param, ex.getMessage());
    }

    /**
     * 处理 SpringMVC 参数校验不正确
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseResult<?> methodArgumentNotValidExceptionExceptionHandler(MethodArgumentNotValidException ex) {
        log.error(ExceptionKit.buildErrorMessage(ex));
        FieldError fieldError = ex.getBindingResult().getFieldError();
        return ResponseResult.fail(DefaultErrorCode.param,
            String.format("请求参数不正确:%s", Objects.requireNonNull(fieldError).getDefaultMessage()));
    }

    /**
     * 400异常
     *
     * @param e .
     * @return .
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResponseResult<?> handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
        log.error(ExceptionKit.buildErrorMessage(e));
        return ResponseResult.fail(DefaultErrorCode.param, String.format("请求参数不可为空:%s", e.getMessage()));
    }

    /**
     * 必填参数校验异常
     *
     * @param e .
     * @return .
     * @apiNote 处理Get请求中 使用@Valid 验证路径中请求实体校验失败后抛出的异常
     */
    @ExceptionHandler(BindException.class)
    public ResponseResult<?> bindException(BindException e) {
        log.error(ExceptionKit.buildErrorMessage(e));
        String message = e.getBindingResult().getAllErrors().stream().map(
            DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining());
        return ResponseResult.fail(DefaultErrorCode.param, message);
    }

    /**
     * 处理请求参数格式错误
     *
     * @param e .
     * @apiNote @RequestParam上validate失败后抛出的异常是ConstraintViolationException
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public ResponseResult<?> constraintViolationException(ConstraintViolationException e) {
        log.error(ExceptionKit.buildErrorMessage(e));
        String message = e.getConstraintViolations().stream().map(ConstraintViolation::getMessage)
            .collect(Collectors.joining());
        return ResponseResult.fail(DefaultErrorCode.param, message);
    }

    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseResult<?> illegalArgumentHandler(IllegalArgumentException e) {
        log.error(ExceptionKit.buildErrorMessage(e));
        return ResponseResult.fail(DefaultErrorCode.param, e.getMessage());
    }

    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public ResponseResult<?> methodArgumentTypeMismatchHandler(MethodArgumentTypeMismatchException e) {
        log.error(ExceptionKit.buildErrorMessage(e));
        return ResponseResult.fail(DefaultErrorCode.param, e.getMessage());
    }

    /**
     * 默认的其他错误信息封装
     *
     * @param ex exception
     * @return response
     */
    @ResponseBody
    @ExceptionHandler(value = {CommException.class})
    public ResponseResult<?> customHandler(CommException ex) {
        // 这里可以对异常统一进行收集处理
        log.error(ExceptionKit.buildErrorMessage(ex));
        String errMsg = StrUtil.blankToDefault(ex.getMessage(), ex.getError().getMessage());
        return ResponseResult.fail(ex.getError().getCode(), errMsg);
    }

    /**
     * 漏底异常
     *
     * @param e exception
     * @return response
     */
    @ResponseBody
    @ExceptionHandler(Exception.class)
    public ResponseResult<?> seriousHandler(Exception e) {
        log.error("seriousHandler", e);
        return ResponseResult.fail(DefaultErrorCode.SERVER_INTERNAL, e.getMessage());
    }
}